<template>
  <li :class="getClass">
    <template v-if="!getCollapse">
      <div :class="`${prefixCls}-submenu-title`" @click.stop="handleClick" :style="getItemStyle">
        <slot name="title"></slot>
        <Icon
          icon="eva:arrow-ios-downward-outline"
          :size="14"
          :class="`${prefixCls}-submenu-title-icon`"
        />
      </div>
      <CollapseTransition>
        <ul :class="prefixCls" v-show="opened">
          <slot></slot>
        </ul>
      </CollapseTransition>
    </template>

    <Popover
      placement="right"
      :overlayClassName="`${prefixCls}-menu-popover`"
      v-else
      :visible="getIsOpend"
      @visible-change="handleVisibleChange"
      :overlayStyle="getOverlayStyle"
      :align="{ offset: [0, 0] }"
    >
      <div :class="getSubClass" v-bind="getEvents(false)">
        <div
          :class="[
            {
              [`${prefixCls}-submenu-popup`]: !getParentSubMenu,
              [`${prefixCls}-submenu-collapsed-show-tit`]: collapsedShowTitle,
            },
          ]"
        >
          <slot name="title"></slot>
        </div>
        <Icon
          v-if="getParentSubMenu"
          icon="eva:arrow-ios-downward-outline"
          :size="14"
          :class="`${prefixCls}-submenu-title-icon`"
        />
      </div>
      <!-- eslint-disable-next-line -->
      <template #content v-show="opened">
        <div v-bind="getEvents(true)">
          <ul :class="[prefixCls, `${prefixCls}-${getTheme}`, `${prefixCls}-popup`]">
            <slot></slot>
          </ul>
        </div>
      </template>
    </Popover>
  </li>
</template>

<script lang="ts">
  import type { CSSProperties, PropType } from 'vue'
  import type { SubMenuProvider } from './types'
  import {
    defineComponent,
    computed,
    unref,
    getCurrentInstance,
    toRefs,
    reactive,
    provide,
    onBeforeMount,
    inject,
  } from 'vue'
  import { useDesign } from '../../../../hooks/web/useDesign'
  import { propTypes } from '/@/utils/propTypes'
  import { useMenuItem } from './useMenu'
  import { useSimpleRootMenuContext } from './useSimpleMenuContext'
  import { CollapseTransition } from '/@/components/Transition'
  import Icon from '/@/components/Icon'
  import { Popover } from 'ant-design-vue'
  import { isBoolean, isObject } from '/@/utils/is'
  import mitt from '/@/utils/mitt'

  const DELAY = 200
  export default defineComponent({
    name: 'SubMenu',
    components: {
      Icon,
      CollapseTransition,
      Popover,
    },
    props: {
      name: {
        type: [String, Number] as PropType<string | number>,
        required: true,
      },
      disabled: propTypes.bool,
      collapsedShowTitle: propTypes.bool,
    },
    setup(props) {
      const instance = getCurrentInstance()

      const state = reactive({
        active: false,
        opened: false,
      })

      const data = reactive({
        timeout: null as TimeoutHandle | null,
        mouseInChild: false,
        isChild: false,
      })

      const { getParentSubMenu, getItemStyle, getParentMenu, getParentList } = useMenuItem(instance)

      const { prefixCls } = useDesign('menu')

      const subMenuEmitter = mitt()

      const { rootMenuEmitter } = useSimpleRootMenuContext()

      const {
        addSubMenu: parentAddSubmenu,
        removeSubMenu: parentRemoveSubmenu,
        removeAll: parentRemoveAll,
        getOpenNames: parentGetOpenNames,
        isRemoveAllPopup,
        sliceIndex,
        level,
        props: rootProps,
        handleMouseleave: parentHandleMouseleave,
      } = inject<SubMenuProvider>(`subMenu:${getParentMenu.value?.uid}`)!

      const getClass = computed(() => {
        return [
          `${prefixCls}-submenu`,
          {
            [`${prefixCls}-item-active`]: state.active,
            [`${prefixCls}-opened`]: state.opened,
            [`${prefixCls}-submenu-disabled`]: props.disabled,
            [`${prefixCls}-submenu-has-parent-submenu`]: unref(getParentSubMenu),
            [`${prefixCls}-child-item-active`]: state.active,
          },
        ]
      })

      const getAccordion = computed(() => rootProps.accordion)
      const getCollapse = computed(() => rootProps.collapse)
      const getTheme = computed(() => rootProps.theme)

      const getOverlayStyle = computed((): CSSProperties => {
        return {
          minWidth: '200px',
        }
      })

      const getIsOpend = computed(() => {
        const name = props.name
        if (unref(getCollapse)) {
          return parentGetOpenNames().includes(name)
        }
        return state.opened
      })

      const getSubClass = computed(() => {
        const isActive = rootProps.activeSubMenuNames.includes(props.name)
        return [
          `${prefixCls}-submenu-title`,
          {
            [`${prefixCls}-submenu-active`]: isActive,
            [`${prefixCls}-submenu-active-border`]: isActive && level === 0,
            [`${prefixCls}-submenu-collapse`]: unref(getCollapse) && level === 0,
          },
        ]
      })

      function getEvents(deep: boolean) {
        if (!unref(getCollapse)) {
          return {}
        }
        return {
          onMouseenter: handleMouseenter,
          onMouseleave: () => handleMouseleave(deep),
        }
      }

      function handleClick() {
        const { disabled } = props
        if (disabled || unref(getCollapse)) return
        const opened = state.opened

        if (unref(getAccordion)) {
          const { uidList } = getParentList()
          rootMenuEmitter.emit('on-update-opened', {
            opend: false,
            parent: instance?.parent,
            uidList: uidList,
          })
        } else {
          rootMenuEmitter.emit('open-name-change', {
            name: props.name,
            opened: !opened,
          })
        }
        state.opened = !opened
      }

      function handleMouseenter() {
        const disabled = props.disabled
        if (disabled) return

        subMenuEmitter.emit('submenu:mouse-enter-child')

        const index = parentGetOpenNames().findIndex((item) => item === props.name)

        sliceIndex(index)

        const isRoot = level === 0 && parentGetOpenNames().length === 2
        if (isRoot) {
          parentRemoveAll()
        }
        data.isChild = parentGetOpenNames().includes(props.name)
        clearTimeout(data.timeout!)
        data.timeout = setTimeout(() => {
          parentAddSubmenu(props.name)
        }, DELAY)
      }

      function handleMouseleave(deepDispatch = false) {
        const parentName = getParentMenu.value?.props.name
        if (!parentName) {
          isRemoveAllPopup.value = true
        }

        if (parentGetOpenNames().slice(-1)[0] === props.name) {
          data.isChild = false
        }

        subMenuEmitter.emit('submenu:mouse-leave-child')
        if (data.timeout) {
          clearTimeout(data.timeout!)
          data.timeout = setTimeout(() => {
            if (isRemoveAllPopup.value) {
              parentRemoveAll()
            } else if (!data.mouseInChild) {
              parentRemoveSubmenu(props.name)
            }
          }, DELAY)
        }
        if (deepDispatch) {
          if (getParentSubMenu.value) {
            parentHandleMouseleave?.(true)
          }
        }
      }

      onBeforeMount(() => {
        subMenuEmitter.on('submenu:mouse-enter-child', () => {
          data.mouseInChild = true
          isRemoveAllPopup.value = false
          clearTimeout(data.timeout!)
        })
        subMenuEmitter.on('submenu:mouse-leave-child', () => {
          if (data.isChild) return
          data.mouseInChild = false
          clearTimeout(data.timeout!)
        })

        rootMenuEmitter.on(
          'on-update-opened',
          (data: boolean | (string | number)[] | Recordable) => {
            if (unref(getCollapse)) return
            if (isBoolean(data)) {
              state.opened = data
              return
            }
            if (isObject(data) && rootProps.accordion) {
              const { opend, parent, uidList } = data as Recordable
              if (parent === instance?.parent) {
                state.opened = opend
              } else if (!uidList.includes(instance?.uid)) {
                state.opened = false
              }
              return
            }

            if (props.name && Array.isArray(data)) {
              state.opened = (data as (string | number)[]).includes(props.name)
            }
          },
        )

        rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => {
          if (instance?.uid) {
            state.active = data.includes(instance?.uid)
          }
        })
      })

      function handleVisibleChange(visible: boolean) {
        state.opened = visible
      }

      // provide
      provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
        addSubMenu: parentAddSubmenu,
        removeSubMenu: parentRemoveSubmenu,
        getOpenNames: parentGetOpenNames,
        removeAll: parentRemoveAll,
        isRemoveAllPopup,
        sliceIndex,
        level: level + 1,
        handleMouseleave,
        props: rootProps,
      })

      return {
        getClass,
        prefixCls,
        getCollapse,
        getItemStyle,
        handleClick,
        handleVisibleChange,
        getParentSubMenu,
        getOverlayStyle,
        getTheme,
        getIsOpend,
        getEvents,
        getSubClass,
        ...toRefs(state),
        ...toRefs(data),
      }
    },
  })
</script>
